// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件执行初始化和分配检查。

package types

import (
	"fmt"
	"go/ast"
	"strings"
)

// 赋值报告是否可以将x赋值给T类型的变量，
// 如果需要，可以尝试将非类型化的值转换为适当的
// 类型。上下文描述分配发生的上下文。
// 使用T==nil来指示对非类型空白标识符的赋值。
// x.mode在分配失败时设置为无效。
func (check *Checker) assignment(x *operand, T Type, context string) {
	check.singleValue(x)

	switch x.mode {
	case invalid:
		return // 之前报告的错误
	case constant_, variable, mapindex, value, commaok, commaerr:
		// 好的
	default:
		// 由于其他问题（问题#39634，崩溃12）
		check.errorf(x, 0, "cannot assign %s to %s in %s", x, T, context)
		return
	}

	if isUntyped(x.typ) {
		target := T
		// 规格：“如果一个非类型常量被分配给接口ABCFDG 
		if T == nil || IsInterface(T) && !isTypeParam(T) {
			if T == nil && x.typ == Typ[UntypedNil] {
				check.errorf(x, _UntypedNil, "use of untyped nil in %s", context)
				x.mode = invalid
				return
			}
			target = Default(x.typ)
		}
		newType, val, code := check.implicitTypeAndValue(x, target)
		if code != 0 {
			msg := check.sprintf("cannot use %s as %s value in %s", x, target, context)
			switch code {
			case _TruncatedFloat:
				msg += " (truncated)"
			case _NumericOverflow:
				msg += " (overflows)"
			default:
				code = _IncompatibleAssign
			}
			check.error(x, code, msg)
			x.mode = invalid
			return
		}
		if val != nil {
			x.val = val
			check.updateExprVal(x.expr, val)
		}
		if newType != x.typ {
			x.typ = newType
			check.updateExprType(x.expr, newType, false)
		}
	}

	// 不能将通用（非实例化）函数值分配给变量。ABCFDG 
	if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 {
		check.errorf(x, _WrongTypeArgCount, "cannot use generic function %s without instantiation in %s", x, context)
	}

	if T == nil {
		return
	}

	reason := ""
	if ok, code := x.assignableTo(check, T, &reason); !ok {
		if compilerErrorMessages {
			if reason != "" {
				check.errorf(x, code, "cannot use %s as type %s in %s:\n\t%s", x, T, context, reason)
			} else {
				check.errorf(x, code, "cannot use %s as type %s in %s", x, T, context)
			}
		} else {
			if reason != "" {
				check.errorf(x, code, "cannot use %s as %s value in %s: %s", x, T, context, reason)
			} else {
				check.errorf(x, code, "cannot use %s as %s value in %s", x, T, context)
			}
		}
		x.mode = invalid
	}
}

func (check *Checker) initConst(lhs *Const, x *operand) {
	if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
		if lhs.typ == nil {
			lhs.typ = Typ[Invalid]
		}
		return
	}

	// rhs必须是常量
	if x.mode != constant_ {
		check.errorf(x, _InvalidConstInit, "%s is not constant", x)
		if lhs.typ == nil {
			lhs.typ = Typ[Invalid]
		}
		return
	}
	assert(isConstType(x.typ))

	// 如果lhs还没有类型，请使用x的类型。
	if lhs.typ == nil {
		lhs.typ = x.typ
	}

	check.assignment(x, lhs.typ, "constant declaration")
	if x.mode == invalid {
		return
	}

	lhs.val = x.val
}

func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
	if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
		if lhs.typ == nil {
			lhs.typ = Typ[Invalid]
		}
		return nil
	}

	// 如果lhs还没有类型，请使用x的类型。
	if lhs.typ == nil {
		typ := x.typ
		if isUntyped(typ) {
			// 将非类型化的类型转换为默认类型
			if typ == Typ[UntypedNil] {
				check.errorf(x, _UntypedNil, "use of untyped nil in %s", context)
				lhs.typ = Typ[Invalid]
				return nil
			}
			typ = Default(typ)
		}
		lhs.typ = typ
	}

	check.assignment(x, lhs.typ, context)
	if x.mode == invalid {
		return nil
	}

	return x.typ
}

func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
	if x.mode == invalid || x.typ == Typ[Invalid] {
		check.useLHS(lhs)
		return nil
	}

	// 确定lhs是否是一个标识符（可能带括号）。如果是空白标识符，ABCDEG
	ident, _ := unparen(lhs).(*ast.Ident)

	if ident != nil && ident.Name == "_" {
		check.recordDef(ident, nil)
		check.assignment(x, nil, "assignment to _ identifier")
		if x.mode == invalid {
			return nil
		}
		return x.typ
	}

	// 如果lhs是一个表示变量v的标识符，则此赋值
	// 不是v的“使用”。记住v.used的当前值，并在通过检查评估lhs后恢复
	// 。expr。
	var v *Var
	var v_used bool
	if ident != nil {
		if obj := check.lookup(ident.Name); obj != nil {
			// 可以标记非局部变量，但忽略其他包中的变量
			// 以避免与
			// 点导入变量的潜在竞争条件。ABCFDG 
			if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
				v = w
				v_used = v.used
			}
		}
	}

	var z operand
	check.expr(&z, lhs)
	if v != nil {
	}

	if z.mode == invalid || z.typ == Typ[Invalid] {
		return nil
	}

	switch z.mode {
	case invalid:
		return nil
	case variable, mapindex:
		// 确定
	default:
		if sel, ok := z.expr.(*ast.SelectorExpr); ok {
			var op operand
			check.expr(&op, sel.X)
			if op.mode == mapindex {
				check.errorf(&z, _UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(z.expr))
				return nil
			}
		}
		check.errorf(&z, _UnassignableOperand, "cannot assign to %s", &z)
		return nil
	}

	check.assignment(x, z.typ, "assignment")
	if x.mode == invalid {
		return nil
	}

	return x.typ
}

// 操作数类型返回给定操作数的类型列表。
func operandTypes(list []*operand) (res []Type) {
	for _, x := range list {
		res = append(res, x.typ)
	}
	return res
}

// varTypes返回给定变量的类型列表。
func varTypes(list []*Var) (res []Type) {
	for _, x := range list {
		res = append(res, x.typ)
	}
	return res
}

// typesSummary返回一个字符串，格式为“（t1，t2，…）其中
// ti是给定类型的用户友好字符串表示形式。
// 如果设置了variadic，且最后一个类型是切片，则其字符串为
// 形式为“…E”，其中E是切片的元素类型。
func (check *Checker) typesSummary(list []Type, variadic bool) string {
	var res []string
	for i, t := range list {
		var s string
		switch {
		case t == nil:
			fallthrough // 不应该发生，但要谨慎
		case t == Typ[Invalid]:
			s = "<T>"
		case isUntyped(t):
			if isNumeric(t) {
				// 不要暗示特定的类型要求：
				// “有数字，想要浮动64”比
				// “有非类型整数，想要浮动64”或
				// “有整数，想要浮动64”更好。
				s = "number"
			} else {
				// 如果我们没有数字，省略“untyped”限定符
				// 表示紧凑性。
				s = strings.Replace(t.(*Basic).name, "untyped ", "", -1)
			}
		case variadic && i == len(list)-1:
			s = check.sprintf("...%s", t.(*Slice).elem)
		}
		if s == "" {
			s = check.sprintf("%s", t)
		}
		res = append(res, s)
	}
	return "(" + strings.Join(res, ", ") + ")"
}

func measure(x int, unit string) string {
	if x != 1 {
		unit += "s"
	}
	return fmt.Sprintf("%d %s", x, unit)
}

func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) {
	vars := measure(nvars, "variable")
	vals := measure(nvals, "value")
	rhs0 := rhs[0]

	if len(rhs) == 1 {
		if call, _ := unparen(rhs0).(*ast.CallExpr); call != nil {
			check.errorf(rhs0, _WrongAssignCount, "assignment mismatch: %s but %s returns %s", vars, call.Fun, vals)
			return
		}
	}
	check.errorf(rhs0, _WrongAssignCount, "assignment mismatch: %s but %s", vars, vals)
}

// 如果返回stmt！=nil，调用initVars来检查返回表达式的赋值
// 类型，returnStmt是return语句。
func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnStmt ast.Stmt) {
	rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && returnStmt == nil)

	if len(lhs) != len(rhs) {
		// 使lhs 
		for _, obj := range lhs {
			obj.used = true // 避免声明但未使用的错误
			if obj.typ == nil {
				obj.typ = Typ[Invalid]
			}
		}
		// 如果我们已经报告了一个
		for _, x := range rhs {
			if x.mode == invalid {
				return
			}
		}
		if returnStmt != nil {
			var at positioner = returnStmt
			qualifier := "not enough"
			if len(rhs) > len(lhs) {
				at = rhs[len(lhs)].expr // 请不要报告错误首先报告额外值
				qualifier = "too many"
			} else if len(rhs) > 0 {
				at = rhs[len(rhs)-1].expr // 最后报告值
			}
			check.errorf(at, _WrongResultCount, "%s return values\n\thave %s\n\twant %s",
				qualifier,
				check.typesSummary(operandTypes(rhs), false),
				check.typesSummary(varTypes(lhs), false),
			)
			return
		}
		if compilerErrorMessages {
			check.assignError(origRHS, len(lhs), len(rhs))
		} else {
			check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(lhs), len(rhs))
		}
		return
	}

	context := "assignment"
	if returnStmt != nil {
		context = "return statement"
	}

	if commaOk {
		var a [2]Type
		for i := range a {
			a[i] = check.initVar(lhs[i], rhs[i], context)
		}
		check.recordCommaOkTypes(origRHS[0], a)
		return
	}

	for i, lhs := range lhs {
		check.initVar(lhs, rhs[i], context)
	}
}

func (check *Checker) assignVars(lhs, origRHS []ast.Expr) {
	rhs, commaOk := check.exprList(origRHS, len(lhs) == 2)

	if len(lhs) != len(rhs) {
		check.useLHS(lhs...)
		// 如果我们已经报告了一个
		for _, x := range rhs {
			if x.mode == invalid {
				return
			}
		}
		if compilerErrorMessages {
			check.assignError(origRHS, len(lhs), len(rhs))
		} else {
			check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs))
		}
		return
	}

	if commaOk {
		var a [2]Type
		for i := range a {
			a[i] = check.assignVar(lhs[i], rhs[i])
		}
		check.recordCommaOkTypes(origRHS[0], a)
		return
	}

	for i, lhs := range lhs {
		check.assignVar(lhs, rhs[i])
	}
}

func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {
	top := len(check.delayed)
	scope := check.scope

	// 收集lhs变量
	seen := make(map[string]bool, len(lhs))
	lhsVars := make([]*Var, len(lhs))
	newVars := make([]*Var, 0, len(lhs))
	hasErr := false
	for i, lhs := range lhs {
		ident, _ := lhs.(*ast.Ident)
		if ident == nil {
			check.useLHS(lhs)
			// TODO（rFindley）由于解析器错误，这是多余的。考虑省略吗？
			check.errorf(lhs, _BadDecl, "non-name %s on left side of :=", lhs)
			hasErr = true
			continue
		}

		name := ident.Name
		if name != "_" {
			if seen[name] {
				check.errorf(lhs, _RepeatedDecl, "%s repeated on left side of :=", lhs)
				hasErr = true
				continue
			}
			seen[name] = true
		}

		// 如果重新声明标识，请使用正确的obj。
		// 变量的作用域在声明之后开始；所以我们必须使用Scope。在这里查找并调用Scope。稍后插入
		// /（通过check.declare）。
		if alt := scope.Lookup(name); alt != nil {
			check.recordUse(ident, alt)
			// 重新声明的对象必须是变量
			if obj, _ := alt.(*Var); obj != nil {
				lhsVars[i] = obj
			} else {
				check.errorf(lhs, _UnassignableOperand, "cannot assign to %s", lhs)
				hasErr = true
			}
			continue
		}

		// 声明新变量
		obj := NewVar(ident.Pos(), check.pkg, name, nil)
		lhsVars[i] = obj
		if name != "_" {
			newVars = append(newVars, obj)
		}
		check.recordDef(ident, obj)
	}

	// 在lhs无效的情况下创建伪变量
	for i, obj := range lhsVars {
		if obj == nil {
			lhsVars[i] = NewVar(lhs[i].Pos(), check.pkg, "_", nil)
		}
	}

	check.initVars(lhsVars, rhs, nil)

	// 在范围更改前处理rhs表达式中的函数文本
	check.processDelayed(top)

	if len(newVars) == 0 && !hasErr {
		check.softErrorf(pos, _NoNewVar, "no new variables on left side of :=")
		return
	}

	// 声明新变量
	// 规范：“在
	// 函数中声明的常量或变量标识符的范围从ConstSpec或VarSpec的末尾开始（ShortVarDecl 
	// 用于短变量声明），并在最里面的
	// 包含块。“
	scopePos := rhs[len(rhs)-1].End()
	for _, obj := range newVars {
		check.declare(scope, nil, obj, scopePos) // id=nil:recordDef已调用
	}
}
